import socket
import struct
import threading
from datetime import datetime

from PyQt5.QtCore import pyqtSignal, QObject

from . import StopThreading


def get_timestamp():
    """获取当前时间戳"""
    return datetime.now().strftime("(%H:%M:%S)")


def match_ip_pattern(client_ip: str, pattern: str) -> bool:
    """
    检查客户端IP是否匹配指定的模式
    :param client_ip: 客户端IP地址
    :param pattern: IP模式，支持通配符*
    :return: 是否匹配
    """
    if not pattern or pattern.strip() == "":
        return True  # 空模式接受所有IP
    
    pattern = pattern.strip()
    
    # 如果模式是单独的*，接受所有IP
    if pattern == "*":
        return True
    
    # 如果模式不包含*，进行精确匹配
    if "*" not in pattern:
        return client_ip == pattern
    
    # 处理通配符模式
    # 将模式转换为正则表达式模式
    import re
    # 转义点号，将*替换为匹配IP段的模式
    regex_pattern = pattern.replace(".", r"\.").replace("*", r"[0-9]+(?:\.[0-9]+)*")
    # 确保完全匹配
    regex_pattern = f"^{regex_pattern}$"
    
    try:
        return bool(re.match(regex_pattern, client_ip))
    except re.error:
        # 如果正则表达式有错误，回退到精确匹配
        return client_ip == pattern


def get_host_ip():
    """
    获取本机IP地址
    :return: 本机IP地址
    """
    try:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("127.0.0.1", 80))
        ip = s.getsockname()[0]
    finally:
        s.close()
    return ip


class UdpMulticastLogic(QObject):
    """
    UDP组播逻辑类
    """
    udp_multicast_signal_write_info = pyqtSignal(str, int)
    udp_multicast_signal_write_msg = pyqtSignal(str)

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.udp_socket = None
        self.sever_th = None
        self.client_th = None
        self.link_flag = self.NoLink  # 用于标记是否开启了连接
        self.address = None
        self.allowed_client_ip = ""  # 允许的客户端IP地址，空字符串表示接受所有IP
        self._server_running = False  # 服务端运行状态标志
        self.udp_multicast_socket = None
        self.multicast_server_th = None
        self.multicast_client_th = None
        self.multicast_group = None
        self.multicast_port = None

    def udp_multicast_server_start(self, port: int, multicast_group: str, allowed_client_ip: str = "", interface_ip: str = "0.0.0.0") -> None:
        """
        开启UDP组播服务端
        :param port: 端口号
        :param multicast_group: 组播地址
        :param allowed_client_ip: 允许的客户端IP地址，空字符串表示接受所有IP
        :param interface_ip: 网卡接口IP地址，用于指定接收组播的网卡
        """
        try:
            self.allowed_client_ip = allowed_client_ip  # 设置IP过滤
            self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            # 设置socket选项，允许端口复用
            self.udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # 绑定端口
            self.udp_socket.bind(('', port))
            # 加入组播组，指定网卡接口
            group = socket.inet_aton(multicast_group)
            # 使用指定的网卡接口IP，而不是默认的0.0.0.0
            interface = socket.inet_aton(interface_ip) if interface_ip != "0.0.0.0" else socket.inet_aton('0.0.0.0')
            mreq = group + interface
            self.udp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_ADD_MEMBERSHIP, mreq)
            
            self.address = (multicast_group, port)
            self.link_flag = self.ServerUDPMulticast
            self._server_running = True  # 设置服务端运行状态
            
            # 创建接收线程
            self.sever_th = threading.Thread(target=self.udp_multicast_server_concurrency)
            self.sever_th.start()
            interface_info = f"，网卡接口:{interface_ip}" if interface_ip != "0.0.0.0" else "，使用默认网卡"
            msg = f"{get_timestamp()} UDP组播服务端已启动，组播地址:{multicast_group}，端口:{port}{interface_info}\n"
            self.udp_multicast_signal_write_msg.emit(msg)
        except Exception as ret:
            self._server_running = False  # 启动失败时重置状态
            msg = f"{get_timestamp()} UDP组播服务端启动失败: {str(ret)}\n"
            self.udp_multicast_signal_write_msg.emit(msg)

    def udp_multicast_server_concurrency(self) -> None:
        """
        用于创建一个线程持续监听UDP组播端口
        """
        while self._server_running:
            try:
                recv_msg, recv_addr = self.udp_socket.recvfrom(1024)
            except OSError as e:
                # 检查是否是因为socket被关闭导致的异常
                if not self._server_running:
                    # 正常关闭，不显示错误信息
                    break
                else:
                    # 异常关闭，显示错误信息
                    msg = f"{get_timestamp()} UDP组播服务端接收失败: {str(e)}\n"
                    self.udp_multicast_signal_write_msg.emit(msg)
                    break
            except Exception as ret:
                # 其他异常
                if self._server_running:
                    msg = f"{get_timestamp()} UDP组播服务端接收异常: {str(ret)}\n"
                    self.udp_multicast_signal_write_msg.emit(msg)
                break
            
            try:
                # IP过滤逻辑
                client_ip = recv_addr[0]
                if not match_ip_pattern(client_ip, self.allowed_client_ip):
                    # 如果客户端IP不匹配过滤模式，则忽略此消息
                    continue
                
                info = recv_msg.decode("utf-8")
                msg = f"{get_timestamp()} 来自IP:{recv_addr[0]}端口:{recv_addr[1]}:"
                self.udp_multicast_signal_write_msg.emit(msg)
                self.udp_multicast_signal_write_info.emit(info, self.InfoRec)
            except UnicodeDecodeError:
                # 处理非UTF-8编码的数据
                try:
                    info = recv_msg.decode("gbk")
                    msg = f"{get_timestamp()} 来自IP:{recv_addr[0]}端口:{recv_addr[1]}:"
                    self.udp_multicast_signal_write_msg.emit(msg)
                    self.udp_multicast_signal_write_info.emit(info, self.InfoRec)
                except:
                    # 如果仍然无法解码，显示十六进制
                    info = recv_msg.hex()
                    msg = f"{get_timestamp()} 来自IP:{recv_addr[0]}端口:{recv_addr[1]} (HEX):"
                    self.udp_multicast_signal_write_msg.emit(msg)
                    self.udp_multicast_signal_write_info.emit(info, self.InfoRec)
            except Exception as ret:
                # 其他处理异常，记录但不中断接收
                if self._server_running:
                    msg = f"{get_timestamp()} 消息处理异常: {str(ret)}\n"
                    self.udp_multicast_signal_write_msg.emit(msg)

    def udp_multicast_client_start(self, ip: str, port: int, multicast_group: str) -> None:
        """
        启动UDP组播客户端
        :param ip: 本机IP
        :param port: 端口号
        :param multicast_group: 组播地址
        """
        try:
            self.udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            # 设置socket选项，允许端口复用
            self.udp_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
            # 设置TTL值
            self.udp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
            
            # 尝试设置组播源地址，如果失败则使用默认接口
            try:
                # 只有当ip不是'0.0.0.0'或空时才设置特定接口
                if ip and ip != '0.0.0.0':
                    self.udp_socket.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_IF, socket.inet_aton(ip))
            except Exception as e:
                # 如果设置特定接口失败，使用默认接口
                msg = f"{get_timestamp()} 警告: 无法设置组播接口 {ip}，使用默认接口: {str(e)}\n"
                self.udp_multicast_signal_write_msg.emit(msg)
            
            self.address = (multicast_group, port)
            self.link_flag = self.ClientUDPMulticast
            
            msg = f"{get_timestamp()} UDP组播客户端已启动，组播地址:{multicast_group}，端口:{port}\n"
            self.udp_multicast_signal_write_msg.emit(msg)
        except Exception as ret:
            msg = f"{get_timestamp()} UDP组播客户端启动失败: {str(ret)}\n"
            self.udp_multicast_signal_write_msg.emit(msg)

    def udp_multicast_send(self, send_info: str) -> None:
        """
        UDP组播发送消息
        :param send_info: 发送的消息
        """
        try:
            # 检查socket是否已创建
            if not hasattr(self, 'udp_socket') or self.udp_socket is None:
                msg = f"{get_timestamp()} 发送失败: UDP组播客户端未启动\n"
                self.udp_multicast_signal_write_msg.emit(msg)
                return
                
            # 检查是否为组播客户端
            if self.link_flag != self.ClientUDPMulticast:
                msg = f"{get_timestamp()} 发送失败: 当前不是UDP组播客户端模式\n"
                self.udp_multicast_signal_write_msg.emit(msg)
                return
                
            send_info_encoded = send_info.encode("utf-8")
            self.udp_socket.sendto(send_info_encoded, self.address)
            msg = f"{get_timestamp()} UDP组播客户端已发送"
            print(msg)
            self.udp_multicast_signal_write_msg.emit(msg)
            self.udp_multicast_signal_write_info.emit(send_info, self.InfoSend)
        except OSError as e:
            if e.winerror == 10051:
                msg = f"{get_timestamp()} 发送失败: 网络不可达 (错误代码: {e.winerror})\n"
                msg += "建议解决方案:\n"
                msg += "1. 检查组播地址是否正确 (建议使用 224.1.1.1)\n"
                msg += "2. 检查网络适配器是否启用\n"
                msg += "3. 检查防火墙设置\n"
                msg += "4. 尝试以管理员身份运行程序\n"
            else:
                msg = f"{get_timestamp()} 发送失败: {str(e)}\n"
            self.udp_multicast_signal_write_msg.emit(msg)
        except Exception as ret:
            msg = f"{get_timestamp()} 发送失败: {str(ret)}\n"
            self.udp_multicast_signal_write_msg.emit(msg)

    def udp_multicast_close(self) -> None:
        """
        关闭UDP组播连接
        """
        # 设置服务端运行状态为False，优雅地停止接收线程
        if hasattr(self, '_server_running'):
            self._server_running = False
        
        try:
            self.udp_socket.close()
        except Exception as ret:
            pass
        
        try:
            if self.link_flag == self.ServerUDPMulticast:
                # 等待接收线程自然结束，而不是强制停止
                if hasattr(self, 'sever_th') and self.sever_th.is_alive():
                    self.sever_th.join(timeout=1.0)  # 等待最多1秒
                    if self.sever_th.is_alive():
                         # 如果线程仍然活跃，则强制停止
                         stop_thread(self.sever_th)
                msg = f"{get_timestamp()} UDP组播服务端已断开连接\n"
                self.udp_multicast_signal_write_msg.emit(msg)
            elif self.link_flag == self.ClientUDPMulticast:
                msg = f"{get_timestamp()} UDP组播客户端已断开连接\n"
                self.udp_multicast_signal_write_msg.emit(msg)
        except Exception as ret:
            pass

    NoLink = -1
    ServerUDPMulticast = 5
    ClientUDPMulticast = 6
    InfoSend = 0
    InfoRec = 1